﻿//*** Guidance generated code ***//
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Security.Principal;
using System.Threading;
using System.Web;
using System.Web.Configuration;
using System.Web.Security;

using ProjectBase.Core;
using ProjectBase.Core.Service;
using ProjectBase.Core.Web;

using Survey.Common.Business.Services;
using Survey.Entity.Services;

namespace Survey.Common.Providers
{
    public class SecurityProvider : ISecurityProvider
    {
        #region members

        private const string NotSigned = "NotSigned";

        private IMenuConfigProvider _menuConfigProvider;

        private bool? _isAuthenticationModeForms;
        private bool? _isAuthenticationModeNone;

        #endregion members

        public virtual bool HasRole(string roleName)
        {
            return Global.Settings.UseCurrentRole
                ? User.CurrentRole.IsEqual(roleName)
                : GetRolesForUser().Any(r => r.Equals(roleName, StringComparison.Ordinal));
        }

        public virtual User User
        {
            get { return GetUser(); }
        }

        #region protected User

        protected virtual User GetUser()
        {
            if (Identity.IsAuthenticated)
            {
                return GetAuthenticatedUser();
            }
            else
            {
                return GetNotAuthenticatedUser();
            }
        }

        protected virtual Entity.Services.User GetNotAuthenticatedUser()
        {
            var user = HttpContext.Current.Session()[GetType().FullName + "User"] as User;

            // is cached for this request
            if (user.IsNull())
            {
                user = UserFacade.CreateNew(NotSigned);

                //var role = IsAuthenticationModeForms || IsAuthenticationModeNone
                //    ? Viewer
                //    : NotSigned;
                var role = NotSigned;
                user.Roles.Add(role);

                HttpContext.Current.Session()[GetType().FullName + "User"] = user;

                // keep it until end of the request
            }
            return user;
        }

        protected virtual Entity.Services.User GetAuthenticatedUser()
        {
            var user = HttpContext.Current.Items[GetType().FullName + "User"] as User; // is cached for this request
            if (user.Is())
            {
                return user;
            }

            user = UserFacade.GetByLogin(Identity.Name);
            if (user.IsNull())
            {
                user = HttpContext.Current.Session()[GetType().FullName + "AuthUser"] as User;

                // is cached in session authentiated
                if (user.IsNull())
                {
                    user = UserFacade.CreateNew(Identity.Name);
                    HttpContext.Current.Session()[GetType().FullName + "AuthUser"] = user;

                    // keep it until end of the request
                }
            }

            user.Roles = new List<string>(SecurityManager.GetRolesForUser(Identity.Name));
            if (user.Roles.IsEmpty())
            {
                user.Roles.Add(NotSigned);
            }
            HttpContext.Current.Items[GetType().FullName + "User"] = user; // keep it until end of the request

            return user;
        }

        #endregion protected User

        public virtual void ChangeCurrentRole(string role)
        {
            if (SecurityManager.GetRolesForUser().Contains(role))
            {
                var user = GetUser();
                user.CurrentRole = role;
                UserFacade.Update(user);
            }
        }

        public virtual IEnumerable<IControllerAccess> GetGrantedControllers()
        {
            return LoadGrantedMenuItems(false);
        }

        public virtual IEnumerable<ITVDataItem> GetGrantedMenuNodes()
        {
            var grantedMenuItems = LoadGrantedMenuItems(true)
                .ToList();

            // append this list to every item - as a data source for children
            grantedMenuItems
                .ForEach(mu => mu.GrantedMenuItems = grantedMenuItems);
            return grantedMenuItems;
        }

        public virtual IEnumerable<string> GetExpandedMenuNodes()
        {
            return MenuConfigProvider
                .MenuItems
                .Where(mi => mi.IsExpanded)
                .Select(mi => mi.ID)
                .ToList();
        }

        public virtual IEnumerable<string> GetApplicationRoles()
        {
            return MenuConfigProvider
                .MenuItems
                .SelectMany(mi => mi.Roles)
                .Select(r => r.RoleName)
                .Distinct();
        }

        public virtual bool IsAuthenticationModeForms
        {
            get
            {
                if (!_isAuthenticationModeForms.HasValue)
                {
                    _isAuthenticationModeForms = false;
                    if (Identity.AuthenticationType.IsNotEmpty())
                    {
                        _isAuthenticationModeForms = Identity.AuthenticationType.Equals(Str.Business.Services.Forms);
                    }
                    else
                    {
                        var authenticationSection = WebConfigurationManager
                                .GetWebApplicationSection(Constants.WebControls.WebConfig.SystemWebAuthentication)
                                as AuthenticationSection;

                        if (authenticationSection.Is())
                        {
                            _isAuthenticationModeForms = authenticationSection.Mode.Equals(AuthenticationMode.Forms);
                        }
                    }
                }
                return _isAuthenticationModeForms.Value;
            }
        }
        public virtual bool IsAuthenticationModeNone
        {
            get
            {
                if (!_isAuthenticationModeNone.HasValue)
                {
                    _isAuthenticationModeNone = false;
                    if (Identity.AuthenticationType.IsNotEmpty())
                    {
                        _isAuthenticationModeNone = Identity.AuthenticationType.Equals(Str.Business.Services.None);
                    }
                    else
                    {
                        var authenticationSection = WebConfigurationManager
                                .GetWebApplicationSection(Constants.WebControls.WebConfig.SystemWebAuthentication)
                                as AuthenticationSection;

                        if (authenticationSection.Is())
                        {
                            _isAuthenticationModeNone = authenticationSection.Mode.Equals(AuthenticationMode.None);
                        }
                    }
                }
                return _isAuthenticationModeNone.Value;
            }
        }

        #region protected MenuItems

        /// <summary>
        ///     Iterates the MenuItems and returnes the filtered copy
        ///     based on user rights
        /// </summary>
        /// <param name = "useIsVisible">will be swithc IsVisible evaluated?</param>
        /// <returns></returns>
        protected virtual IEnumerable<MenuItem> LoadGrantedMenuItems(bool useIsVisible)
        {
            return MenuConfigProvider
                .MenuItems
                .Where(mi => IsGranted(mi, useIsVisible))
                .Select(mi => Global.Settings.UseCurrentRole
                                  ? mi.Clone(User.CurrentRole)
                                  : mi.Clone(GetRolesForUser()));
        }

        protected virtual bool IsGranted(MenuItem menuItem, bool useIsVisible)
        {
            if (Global.Settings.UseCurrentRole)
            {
                return menuItem.Roles.Any(r => (r.IsVisible || !useIsVisible)
                                               && r.RoleName.Equals(
                                                   User.CurrentRole,
                                                   StringComparison.Ordinal));
            }
            return menuItem.Roles.Any(r => (r.IsVisible || !useIsVisible)
                                           && GetRolesForUser().Any(
                                               role =>
                                               role.Equals(r.RoleName, StringComparison.Ordinal)));
        }

        public virtual IEnumerable<string> GetRolesForUser()
        {
            return GetRolesForUser(Identity.Name);
        }

        public virtual IEnumerable<string> GetRolesForUser(string username)
        {
            //var applicationRoles = HttpContext.Current.Session()[this.GetType().FullName + "UserRoles"] as IList<string>;
            var applicationRoles = HttpContext.Current.Items[GetType().FullName + "UserRoles"] as IList<string>;

            if (applicationRoles.IsNull())
            {
                var roles = TryGetRoles(username);
                applicationRoles = new List<string>();
                var map = Global.Settings.RolesConvertor;

                foreach (var role in roles.OrderBy(r => map.ContainsKey(r)
                                                            ? map[r]
                                                            : ("z" + r)))
                {
                    applicationRoles.Add(map.ContainsKey(role)
                                             ? map[role]
                                             : role);
                }
                applicationRoles = applicationRoles.Distinct().ToList();

                //HttpContext.Current.Session()[this.GetType().FullName + "UserRoles"] = applicationRoles;
                HttpContext.Current.Items[GetType().FullName + "UserRoles"] = applicationRoles;
            }
            return applicationRoles;
        }

        protected virtual IEnumerable<string> TryGetRoles(string username)
        {
            try
            {
                return Roles.GetRolesForUser(username);
            }
            catch (NotImplementedException ex)
            {
                PublishingManager.Publish(this, "current RoleProvider does not support GetRolesForUser(username)", ex);
            }
            var roles = new List<string>();
            foreach (var pair in Global.Settings.RolesConvertor)
            {
                if (Roles.Provider.IsUserInRole(username, pair.Key))
                {
                    roles.Add(pair.Value);
                }
            }
            return roles;
        }

        #endregion protected MenuItems

        #region protected properties

        protected virtual IIdentity Identity
        {
            get { return Thread.CurrentPrincipal.Identity; }
        }

        #endregion protected properties

        #region IoC

        public virtual IMenuConfigProvider MenuConfigProvider
        {
            protected get
            {
                if (_menuConfigProvider.IsNull())
                {
                    _menuConfigProvider = Factory.CreateInstance<IMenuConfigProvider>();
                }
                return _menuConfigProvider;
            }
            set { _menuConfigProvider = value; }
        }
        public virtual IUserFacade UserFacade { protected get; set; }

        #endregion IoC
    }
}